Text Mining Kickstarter Projects

Overview

Kickstarter is an American public-benefit corporation based in Brooklyn, New York, that maintains a global crowd funding platform focused on creativity. The company’s stated mission is to “help bring creative projects to life”.

Kickstarter has reportedly received almost $5 billion in pledges from 17.6 million backers to fund 180,000 creative projects, such as films, music, stage shows, comics, journalism, video games, technology and food-related projects.

For this assignment, I analyze the descriptions of kickstarter projects to identify commonalities of successful (and unsuccessful projects) using the text mining techniques.

Data

The dataset for this assignment is taken from webroboto.io ‘s repository. They developed a scrapper robot that crawls all Kickstarter projects monthly since 2009. We will just take data from the most recent crawl on 2020-02-13.

To simplify your task, I have downloaded the files and partially cleaned the scraped data. In particular, I converted several JSON columns, corrected some obvious data issues, and removed some variables that are not of interest (or missing frequently), and remove some duplicated project entries. I have also subsetted the data to only contain projects originating in the United States (to have only English language and USD denominated projects).

The data is contained in the file kickstarter_projects_2020_02.csv and contains about 127k projects and about 20 variables.

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✓ ggplot2 3.2.1     ✓ purrr   0.3.3
✓ tibble  3.0.0     ✓ dplyr   0.8.5
✓ tidyr   1.0.2     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.4.0
package ‘tibble’ was built under R version 3.6.2── Conflicts ────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x ggplot2::%+%()      masks qdapRegex::%+%()
x ggplot2::annotate() masks NLP::annotate()
x dplyr::explain()    masks qdapRegex::explain()
x dplyr::filter()     masks stats::filter()
x dplyr::id()         masks qdapTools::id()
x dplyr::lag()        masks stats::lag()
library(dplyr)
library(ggplot2)
kickstarter <- read_csv('data/kickstarter_projects_2020_02.csv')
Parsed with column specification:
cols(
  .default = col_character(),
  backers_count = col_double(),
  converted_pledged_amount = col_double(),
  created_at = col_date(format = ""),
  deadline = col_date(format = ""),
  goal = col_double(),
  id = col_double(),
  is_starrable = col_logical(),
  launched_at = col_date(format = ""),
  pledged = col_double(),
  spotlight = col_logical(),
  staff_pick = col_logical(),
  state_changed_at = col_date(format = "")
)
See spec(...) for full column specifications.
head(kickstarter)

Tasks for the Assignment

1. Identifying Successful Projects

a) Success by Category

There are several ways to identify success of a project:
- State (state): Whether a campaign was successful or not.
- Pledged Amount (pledged)
- Achievement Ratio: Create a variable achievement_ratio by calculating the percentage of the original monetary goal reached by the actual amount pledged (that is pledged\goal *100).
- Number of backers (backers_count)
- How quickly the goal was reached (difference between launched_at and state_changed_at) for those campaigns that were successful.

Use one or more of these measures to visually summarize which categories were most successful in attracting funding on kickstarter. Briefly summarize your findings.

# Add two columns: achievement_ratio and time difference between `launched_at` and `state_changed_at`
kick <- kickstarter %>%
  mutate(achievement_ratio = pledged/goal * 100) %>%
  mutate(diff_days = as.numeric(state_changed_at - launched_at))
freq <- kick %>%
  group_by(top_category,state) %>%
  summarise(freq = n()) %>%
  group_by(top_category) %>%
  mutate(perc = freq/sum(freq)*100)

freq_suc <- freq %>%
  filter(state == 'successful') %>%
  select(-c(state,freq), perc_suc = perc)

freq <- freq %>%
  left_join(freq_suc, by = 'top_category')
freq$state <- factor(freq$state, levels = c('failed','canceled','suspended','live','successful'))
  1. First, I used State (state) to determine if one project is successful or not. So, I created a stack bar plot and found that dance and comics have high percentage of successful projects.
library(ggthemes)
category_state <- ggplot(freq, aes(fill = state, x = reorder(top_category,perc_suc), y = freq)) +
         geom_bar(position = "fill", stat = "identity", alpha = 0.8) +
         coord_flip() +
        scale_x_discrete() + 
        scale_fill_brewer(palette="BrBG")+
        labs(x= NULL, y = "Percentage of Different Project State", fill = "State")+
        theme_clean()
category_state

  1. Then, I further explore the projects by visualizing their achievement ratio. As we can see, some music projects have rather high achievement ratio. But since the number of these main category projects is too large, the variance is also high. We can find there are relatively fewer dance and comics projects, but these projects rarely have a low achievement ratio. I think these projects have a certain professional skills and unique hobbies, which limit the scale of the groups. But people who prefer these activities are more willing to pay for these projects than the mass.
library(viridis)
Loading required package: viridisLite
category_achieve_ratio <- ggplot(kick, aes(x = top_category, y = achievement_ratio, fill = top_category)) +
  geom_jitter(aes(color = top_category),size = 0.2)+
  scale_color_viridis(discrete=TRUE, option="viridis")+
  scale_y_continuous(trans = 'log10')+
  ggtitle("Achievement Ratio of Different Category Projects")+
  labs(x= "Category", y = "Achievement Ratio") +
  theme_clean()+ 
  theme(axis.text.x = element_text(angle = 30, hjust = 1),
        legend.position = "none",
        plot.title = element_text(hjust=0.5)
        )

category_achieve_ratio

BONUS ONLY: b) Success by Location

Now, use the location information to calculate the total number of successful projects by state (if you are ambitious, normalize by population). Also, identify the Top 50 “innovative” cities in the U.S. (by whatever measure you find plausible). Provide a leaflet map showing the most innovative states and cities in the U.S. on a single map based on these information.

library(openintro)
library(albersusa)
library(sp)
# Calculate the total number of successful projects by state
state_suc <- kick %>%
  filter(state == "successful") %>%
  group_by(location_state) %>%
  summarise(num = n()) %>%
  mutate(state_name = abbr2state(location_state)) 

# Use usa_df()
state_inno <- usa_sf() %>%
  left_join(state_suc, c("name"="state_name"))
Column `name`/`state_name` joining factor and character vector, coercing into character vector
# Normalize by population
state_inno_pop <- state_inno %>%
  mutate(inno_pop = num/pop_2014*10000)

most_inno_city <- kick %>%
  group_by(location_town)%>%
  summarise(avg_achieve_ratio = round(median(achievement_ratio),2)) %>%
  arrange(desc(avg_achieve_ratio))%>%
  head(50)
most_inno_city[most_inno_city$location_town== "34773",]$location_town <- "Saint Cloud"
most_inno_city[most_inno_city$location_town== "32821",]$location_town <- "Orlando"
most_inno_city[most_inno_city$location_town== "19136",]$location_town <- "Philadelphia"
most_inno_city[most_inno_city$location_town== "13361",]$location_town <- "Jordanville"
most_inno_city[most_inno_city$location_town== "Wynwood Art District",]$location_town <- "Wynwood"
# Most innovative 50 cities in the United States
cities <- most_inno_city$location_town
library(geonames)
options(geonamesUsername="lilixueying")
options(geonamesHost="api.geonames.org")

GNsearchUS <- function(x){
  city_df <- GNsearch(name = x, country = "US")
  city_coords <- city_df[1, c("lng", "lat")]
  return(city_coords)
}
GNresult<- lapply(cities, GNsearchUS) 
result <- do.call("rbind", GNresult)
top_inno_cities <- cbind(most_inno_city, result)
# Prepare the cities dataset and provide the lon/lat for cities
top_inno_cities$lng <- as.numeric(top_inno_cities$lng)
top_inno_cities$lat <- as.numeric(top_inno_cities$lat)
head(top_inno_cities)

The color of each state represents the average number of successful projects (Per 10000 Population) in this state. And I calculate the average achievement ratio for each town and subset the top 50 innovative cities to put on the map. Clicking the popup can access more details.

library(leaflet)
spdf <- rmapshaper::ms_simplify(state_inno_pop, keep = 0.1)
pal_response <- colorNumeric("GnBu",domain = 0:7)

epsg2163 <- leafletCRS(
  crsClass = "L.Proj.CRS",
  code = "EPSG:2163",
  proj4def = "+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs",
  resolutions = 2^(16:7))

inno_icons <- icons(
  iconUrl = "data/inno_icon.png",
  iconWidth = 20, iconHeight = 20,
  iconAnchorX = 7.5, iconAnchorY = 8.5
  )

spdf %>%
  leaflet(options = leafletOptions(crs = epsg2163)) %>%
  addPolygons(weight = 1, opacity = 1, color = "#444444", 
    fillColor = ~pal_response(inno_pop), fillOpacity = 0.7, smoothFactor = 0.5,
    label = ~paste(name, ": ",inno_pop, "successful Projects (Per 10k Population)"))%>%
    addMarkers(data = top_inno_cities, lng = ~lng, lat = ~lat, popup = ~paste("Top 50 Innovative Town: ", location_town, "<br>Average Achievement Ratio: ", avg_achieve_ratio), icon = inno_icons)%>%
    addLegend(pal = pal_response, values = 0:7, title = NULL,
            position = "topleft", opacity=0.7)
Some values were outside the color scale and will be treated as NA

2. Writing your success story

Each project contains a blurb – a short description of the project. While not the full description of the project, the short headline is arguably important for inducing interest in the project (and ultimately popularity and success). Let’s analyze the text.

a) Cleaning the Text and Word Cloud

library(tm)       # Framework for text mining.
library(quanteda) # Another great text mining package
library(tidytext) # Text as Tidy Data (good for use with ggplot2)
library(qdap)
library(SnowballC)
  1. To reduce the time for analysis, select the 1000 most successful projects and a sample of 1000 unsuccessful projects.
# Filter those successful projects and pick those with higher achievement ratio
successful <- kick %>%
  filter(state == "successful") %>%
  arrange(desc(achievement_ratio)) %>%
  head(1000)
# Filter those failed projects with lower achievement ratio
fail <- kick %>%
  filter(state == "failed") %>%
  arrange(achievement_ratio) %>%
  head(1000)
all <- as.data.frame(cbind(successful$blurb,fail$blurb), stringsAsFactors = FALSE)
colnames(all) <- c("success","fail")
head(all)
  1. Use the cleaning functions to remove unnecessary words (stop words), syntax, punctuation, numbers, white space etc. Note, that many projects use their own unique brand names in upper cases, so try to remove these fully capitalized words as well (since we are aiming to identify common words across descriptions).
# Dedine a clean_corpus function
clean_corpus <- function(corpus){
  corpus <- tm_map(corpus, content_transformer(function(x){
              processed <- gsub(pattern = '[^a-zA-Z0-9\\s]+', x, replacement = "",
              ignore.case = TRUE,perl = TRUE)
              # Remove fully capitalized words. "\b" means word boundary. 
              processed <- gsub("\\b[A-Z]+\\b","", processed)
              return(processed)}))
  # convert upper case to lower case, remove symbols and (..)
  corpus <- tm_map(corpus, content_transformer(tolower))
  corpus <- tm_map(corpus, content_transformer(replace_symbol))
  corpus <- tm_map(corpus, content_transformer(replace_contraction))
  # remove numbers, puctuations and whitespace
  corpus <- tm_map(corpus, removeNumbers)
  corpus <- tm_map(corpus, removePunctuation)
  corpus <- tm_map(corpus, stripWhitespace)
  # Remove stopwords
  corpus <- tm_map(corpus, removeWords,c(stopwords("en"),"kickstarter", "project", "rd", "st", "th","the"))
  
  return(corpus)
}
# Convert descriptions of successful projects to corpus
s_projects <- VectorSource(all$success)
s_corpus <- VCorpus(s_projects)
s_clean <- clean_corpus(s_corpus)
# Convert descriptions of failed projects to corpus
f_projects <- VectorSource(all$fail)
f_corpus <- VCorpus(f_projects)
f_clean <- clean_corpus(f_corpus)
  1. Stem the words left over and complete the stems.
s_stemed <- tm_map(s_clean, stemDocument)
f_stemed <- tm_map(f_clean, stemDocument)
# Stem completion function
stemCompletion2 <- function(x, dictionary) {
   x <- unlist(strsplit(as.character(x), " "))
   # stemCompletion completes an empty string to a word in dictionary. 
   # Remove empty string to avoid issue.
   x <- x[x != ""]
   x <- stemCompletion(x, dictionary=dictionary)
   x <- paste(x, sep="", collapse=" ")
   PlainTextDocument(stripWhitespace(x))
}
# Stem completion
s_comp <- lapply(s_stemed, stemCompletion2, 
                     dictionary=s_clean)
s_comp_corpus <- as.VCorpus(s_comp)
f_comp <- lapply(f_stemed, stemCompletion2,
                 dictionary =f_clean)
f_comp_corpus <- as.VCorpus(f_comp)
# Compare the result of text stemmed and stem completed
print(s_comp_corpus[[1]]$content)
[1] "The install popular cover friend album say time charm"
print(s_stemed[[1]]$content)
[1] "The instal popular cover friend album say time charm"
print(f_comp_corpus[[15]]$content)
[1] "Just three stories motivates poster inspiration steve job stanford commencement address"
print(f_stemed[[15]]$content)
[1] "Just three stori motiv poster inspir steve job stanford commenc address"
  1. Create a document-term-matrix.
s_dtm <- DocumentTermMatrix(s_comp_corpus)
f_dtm <- DocumentTermMatrix(f_comp_corpus)
print(s_dtm)
<<DocumentTermMatrix (documents: 1000, terms: 3378)>>
Non-/sparse entries: 9906/3368094
Sparsity           : 100%
Maximal term length: 36
Weighting          : term frequency (tf)
print(f_dtm)
<<DocumentTermMatrix (documents: 1000, terms: 3216)>>
Non-/sparse entries: 10480/3205520
Sparsity           : 100%
Maximal term length: 39
Weighting          : term frequency (tf)
  1. Provide a word cloud of the most frequent or important words among the most successful projects.

I chose TFIDF as the weight measure and found the most frequent words (penalized by frequency in all documents). As we can see in this wordcloud, design, game, enamel, art, album, comic, play are most frequent theme mentioned in successful projects’descriptions. Besides, those projects with creative idea are more likely to be successful. For example, we see some words like new, timest, create, first, edition etc. These words indicate that successful projects convey a kind of new experience to people.

# I choose TFIDF to weight words
tfidf_dtm <- DocumentTermMatrix(s_comp_corpus, control = list(weight = weightTfIdf))
tfidf_dtm_m <- as.matrix(tfidf_dtm)
tfidf_weight <- colSums(tfidf_dtm_m)
tfidf_word <- names(tfidf_weight)
library(wordcloud)
pal2 <- brewer.pal(8,"Dark2")
set.seed(1)
wordcloud(tfidf_word, tfidf_weight, 
          min.freq=1,
          max.words=50,
          colors=c("DarkOrange", "CornflowerBlue", "DarkRed"),
          rot.per = 0.3,
          random.order=FALSE,
          random.color=FALSE)

library(wordcloud2)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
df_tfidf <- as.data.frame(cbind(tfidf_word,tfidf_weight),stringsAsFactors = FALSE, row.names = FALSE, )
df_tfidf$tfidf_weight <- as.integer(df_tfidf$tfidf_weight)
wordcloud2(df_tfidf, size=1.6, color='random-dark')

b) Success in words

Provide a pyramid plot to show how the words between successful and unsuccessful projects differ in frequency. A selection of 10 - 20 top words is sufficient here.

# combine descriptions of successful projects and failed projects
all_s <- paste(successful$blurb, collapse = "")
all_f <- paste(fail$blurb, collapse = "")
all_projects <-c(all_s, all_f)
all_corpus <- VCorpus(VectorSource(all_projects))
all_clean <- clean_corpus(all_corpus)
all_tdm <- TermDocumentMatrix(all_clean)
  1. First, I made a polarized wordcloud for successful projects and failed projects. Compared to successful projects, the most common but unique words for failed projects include community, family, local, share, people, which indicates a kind of collectivism and tradition local style. On the other hand, successful projects underscore innovation and individuation.
# Polarized tag cloud
colnames(all_tdm) <- c("success","fail")
all_m <- as.matrix(all_tdm)
comparison.cloud(all_m, colors = c("cyan4", "darkgoldenrod2"), max.words = 40)

  1. Then, I just kept those common words in both successful and failed projects, calculated the difference between the frequency and showed those common words with greatest difference. According to the pyramid plot, we can found successful projects describe more on the projects themselves, like game, comic,edition, cards. However, failed projects try to convey the meaning of the projects, so there are a lot of words like can, help, world, create etc.
library(plotrix)
all_m_df <- as.data.frame(all_m)
all_m_df$terms <- rownames(all_m_df) 
common_words <- all_m_df %>%
  filter(success!=0, fail!=0) %>%
  mutate(diff = abs(success - fail))
top_df <- top_n(common_words, 20, diff)
pyramid.plot(top_df$success, top_df$fail, labels = top_df$terms, gap = 10,
             top.labels = c("Success Projects", "Words", "Fail Projects"),
             main = "Words in Common", unit = NULL, labelcex=0.7)
[1] 5.1 4.1 2.1 2.1

c) Simplicity as a virtue

These blurbs are short in length (max. 150 characters) but let’s see whether brevity and simplicity still matters. Calculate a readability measure (Flesh Reading Ease, Flesh Kincaid or any other comparable measure) for the texts. Visualize the relationship between the readability measure and one of the measures of success. Briefly comment on your finding.

  1. I calculate Flesh, Flesh Kincaid measures. Compare the effects they showed in visualization, I eventually chose to use Flesh Kincaid as the index of readability. The lower the Flesch-Kincaid Grade Level, the easier a piece of text is to read.
library(quanteda)
read_s <- successful%>%
  select(id, blurb, state, top_category, achievement_ratio, diff_days, backers_count)
read_f <- fail%>%
   select(id, blurb, state, top_category, achievement_ratio, diff_days, backers_count)
read_all <- rbind(read_s,read_f)
df_read <- data.frame(doc_id = read_all$id, text = read_all$blurb, read_all[,3:7], stringsAsFactors = FALSE)
df_source <- DataframeSource(df_read)
df_corpus <- VCorpus(df_source)
read_corpus <- corpus(df_corpus)
FRE_read <- textstat_readability(read_corpus,
              measure=c('Flesch', 'Flesch.Kincaid'))
read_score <- data.frame(cbind(read_all,FRE_read))
read_score <-rename(read_score, c("FK" = "Flesch.Kincaid"))
head(read_score)
  1. In this plot, I try to find the relationships between the readability measure and successful projects. From this plot, we can not include that readability has direct relations with successful or failed state. However, we can still get some useful information from the plot. There are two key time, about 30 days and 60 days. This means the time for projects to collect crowd funding is about one month and two months. And we can find successful projects have more backers than that of failed.
read_score %>%
  filter(diff_days != 0) %>%
  ggplot(aes(x = diff_days, y = FK, size = backers_count))+
  geom_point(alpha = 0.5, aes(col = as.factor(state)))+
  scale_size_continuous(range = c(1, 10))+
  labs(x = "Days Between State Change", y = "Flesch-Kincaid Grade Level", size = "Backers Count", col ="State")+
  theme_bw()

  1. I continue to explore the relationships between readability measure and successful projects. As the fitted line shows, when the descriptions become harder to read, the backer counts increased a little bit. But actually, the effect is too tiny to observe directly. Besides, the size of circles represents the achievement ratio of projects. We can find that descriptions of some projects with very high achievement ratio have a low Flesch-Kincaid Grade Level, which means easier to read.
library(ggthemes)
read_score %>%
  filter(backers_count !=0) %>%
  ggplot(aes(x= backers_count, y = FK, size = achievement_ratio))+
  scale_x_continuous(trans = "log10")+
  geom_point(alpha = 0.7, aes(col = top_category))+
  scale_size_continuous(range = c(1, 10))+
  geom_smooth(method = 'gam', col = "black", size = 0.8) +
  guides(size = FALSE) +
  theme_tufte() +
  xlab("Log Backers Count") + ylab("Flesch-Kincaid Grade Level")

3. Sentiment

Now, let’s check whether the use of positive / negative words or specific emotions helps a project to be successful.

a) Stay positive

Calculate the tone of each text based on the positive and negative words that are being used.I use the Bing dictionary contained in the tidytext package (tidytext::sentiments). Visualize the relationship between tone of the document and success.

library(tidytext)
library(broom)
bing <- get_sentiments("bing")
s_sent <- tidy(DocumentTermMatrix(s_clean))
f_sent <- tidy(DocumentTermMatrix(f_clean))
  1. I inner join the bing lexicon to determine if the words in the descritions are positive and negative. After summarizing the total number of possitive words and negative words, I calculated the polarity, which is the difference between number of positive words and negative words for each description.
s_polarity <- s_sent %>%
  inner_join(bing, by = c("term"="word")) %>%
  mutate(index = as.numeric(document)) %>%
  count(sentiment, index) %>%
  spread(sentiment,n,fill = 0) %>%
  mutate(polarity = positive - negative)
f_polarity <- f_sent %>%
  inner_join(bing, by = c("term"="word")) %>%
  mutate(index = as.numeric(document)) %>%
  count(sentiment, index) %>%
  spread(sentiment,n,fill = 0) %>%
  mutate(polarity = positive - negative)
s_polarity$state <- "successful"
f_polarity$state <- "failed"
all_polarity <- rbind(s_polarity,f_polarity)
all_polarity$state <- as.factor(all_polarity$state)
head(all_polarity)
  1. All descriptions, whether successful or failed, show a kind of positive tone since their polarity score are all positive numbers. In average, we can say failed projects have a higher positive tone in descriptions than that of successful projects. This might be a reason for failure, which means the descriptions are too ideal and do not take a lot of real and plausible analysis into account. People are less likely to support a porject with a high goal but lack of specifc plan.
ggplot(all_polarity, aes(index,polarity))+
  geom_smooth(aes(color = state, fill = state), method = "loess") + 
  scale_color_viridis(discrete = TRUE, option = "D")+
  scale_fill_viridis(discrete = TRUE)+
  geom_hline(yintercept = 0, color = "red") +
  ggtitle("Project Description Polarity")+
  labs(x = "Project No.", y="Positive Polarity")+
  theme_economist()+
  theme(plot.title = element_text(hjust=0.5))

b) Positive vs negative

Segregate all 2,000 blurbs into positive and negative texts based on their polarity score calculated in step (a). Now, collapse the positive and negative texts into two larger documents. Create a document-term-matrix based on this collapsed set of two documents. Generate a comparison cloud showing the most-frequent positive and negative words.

s <- successful %>%
  mutate(index = row_number())%>%
  select(blurb, index)
ss <- s_polarity %>%
  left_join(s,by="index")%>%
  select(blurb,polarity)
f <- fail %>%
  mutate(index = row_number())%>%
  select(blurb, index)
ff <- f_polarity %>%
  left_join(f,by="index")%>%
  select(blurb,polarity)
total <- rbind(ss,ff)
total_df <- total %>%
  select(text = blurb, polarity = polarity)
head(total_df)
# Create a function that can split the positive polarity and negative polarity text
# Paste all positive texts as one long string, paste all negative texts as another long string
subsection <- function(df){
  x.pos <- subset(df$text, df$polarity > 0)
  x.neg <- subset(df$text, df$polarity < 0)
  x.pos <- paste(x.pos, collapse = " ")
  x.neg <- paste(x.neg, collapse = " ")
  all.terms <- c(x.pos, x.neg)
  return(all.terms)
}
all_terms <- subsection(total_df)
# Create a polarity wordcloud
polarity_corpus <- all_terms %>%
  VectorSource() %>%
  VCorpus()

all_tdm <-TermDocumentMatrix(polarity_corpus, 
          control = list(removePunctuation = TRUE, stopwords = stopwords("en"))) %>%
            as.matrix()
colnames(all_tdm) <- c("positive", "negative")
polarity_cloud <- comparison.cloud(all_tdm, max.words = 80, colors = c("goldenrod1","darkolivegreen4"))

c) Get in their mind

Now, use the NRC Word-Emotion Association Lexicon in the tidytext package to identify a larger set of emotions (anger, anticipation, disgust, fear, joy, sadness, surprise, trust). Again, visualize the relationship between the use of words from these categories and success. What is your finding?

  1. Get the nrc lexicon
library(textdata)
library(tidytext)

nrc <- get_sentiments("nrc")
head(nrc)
  1. Show all tokens with the state of the project they come from.
s_sent$state <-"successful"
f_sent$state <-"failed"
all_emotion <- rbind(s_sent, f_sent)
head(all_emotion)
  1. Inner join the tokens dataset with nrc lexicon. I dropped those tokens with positive/negative sentiment and just kept those tokens with specific emotions. Then, I calculate the total number of tokens for all emotions in two kinds of state, individually.
scores <- all_emotion %>%
  inner_join(nrc, by = c("term"="word")) %>%
  filter(!grepl("positive|negative",sentiment)) %>%
  count(state, sentiment) %>%
  spread(state,n)
scores
  1. I create a Radar plot that shows 8 emotions. The finding is interesting that the descriptions of failed projects and successful projects share similar shape sentiment radar. They both emphasize more on some emotions like joy, trust and anticipation. But the problem of failed projects is that they amplify the differences between main emotions and other complicated emotions, so they can convey people just a joyful and peaceful feel. On the other hand, successful balance all emotions better. The descriptions show more various and multidimensional emotions, which make the projects seem to more realistic and reliable.
library(radarchart)
library(webshot)
library(htmlwidgets)
library(png)
radarPlot <- chartJSRadar(scores, showToolTipLabel=TRUE, main = "Sentiment Radar")
saveWidget(radarPlot, "radarplt.html")
webshot("radarplt.html")
img <- magick::image_read("webshot.png")
plot(img)

Submission

Please follow the instructions to submit your homework. The homework is due on Thursday, April 16.

Please stay honest!

If you do come across something online that provides part of the analysis / code etc., please no wholesale copying of other ideas. We are trying to evaluate your abilities to visualized data not the ability to do internet searches. Also, this is an individually assigned exercise – please keep your solution to yourself.

LS0tCnRpdGxlOiAiSG9tZXdvcmsgMyAtIFRleHQgQW5hbHlzaXMgZm9yIEtpY2tzdGFydGVyIgphdXRob3I6ICJYdWV5aW5nIEh1YW5nIgpkYXRlOiAnMjAyMC0wNC0xNScKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAga2VlcF9tZDogdHJ1ZQogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCmFsd2F5c19hbGxvd19odG1sOiB5ZXMKLS0tCgpUZXh0IE1pbmluZyBLaWNrc3RhcnRlciBQcm9qZWN0cwo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKYGBge3IgU2V0dXAsIGluY2x1ZGU9RkFMU0UsIHJlc3VsdHM9J2hpZGUnLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpvcHRzX2NodW5rJHNldChmaWcucGF0aD0iZmlndXJlcy8iLAogICAgICAgICAgICAgICBjYWNoZS5wYXRoPSJjYWNoZS8iLAogICAgICAgICAgICAgICBjYWNoZT1GQUxTRSwKICAgICAgICAgICAgICAgZWNobz1UUlVFLAogICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLAogICAgICAgICAgICAgICB3YXJuaW5nPUZBTFNFKSAgCmBgYCAgCgojIyBPdmVydmlldwoKS2lja3N0YXJ0ZXIgaXMgYW4gQW1lcmljYW4gcHVibGljLWJlbmVmaXQgY29ycG9yYXRpb24gYmFzZWQgaW4gQnJvb2tseW4sIE5ldyBZb3JrLCB0aGF0IG1haW50YWlucyBhIGdsb2JhbCBjcm93ZCBmdW5kaW5nIHBsYXRmb3JtIGZvY3VzZWQgb24gY3JlYXRpdml0eS4gIFRoZSBjb21wYW55J3Mgc3RhdGVkIG1pc3Npb24gaXMgdG8gImhlbHAgYnJpbmcgY3JlYXRpdmUgcHJvamVjdHMgdG8gbGlmZSIuIAoKS2lja3N0YXJ0ZXIgaGFzIHJlcG9ydGVkbHkgcmVjZWl2ZWQgYWxtb3N0ICQ1IGJpbGxpb24gaW4gcGxlZGdlcyBmcm9tIDE3LjYgbWlsbGlvbiBiYWNrZXJzIHRvIGZ1bmQgMTgwLDAwMCBjcmVhdGl2ZSBwcm9qZWN0cywgc3VjaCBhcyBmaWxtcywgbXVzaWMsIHN0YWdlIHNob3dzLCBjb21pY3MsIGpvdXJuYWxpc20sIHZpZGVvIGdhbWVzLCB0ZWNobm9sb2d5IGFuZCBmb29kLXJlbGF0ZWQgcHJvamVjdHMuCgpGb3IgdGhpcyBhc3NpZ25tZW50LCBJIGFuYWx5emUgdGhlIGRlc2NyaXB0aW9ucyBvZiBraWNrc3RhcnRlciBwcm9qZWN0cyB0byBpZGVudGlmeSBjb21tb25hbGl0aWVzIG9mIHN1Y2Nlc3NmdWwgKGFuZCB1bnN1Y2Nlc3NmdWwgcHJvamVjdHMpIHVzaW5nIHRoZSB0ZXh0IG1pbmluZyB0ZWNobmlxdWVzLiAKCiMjIERhdGEKClRoZSBkYXRhc2V0IGZvciB0aGlzIGFzc2lnbm1lbnQgaXMgdGFrZW4gZnJvbSBbd2Vicm9ib3RvLmlvIOKAmHMgcmVwb3NpdG9yeV0oaHR0cHM6Ly93ZWJyb2JvdHMuaW8va2lja3N0YXJ0ZXItZGF0YXNldHMvKS4gVGhleSBkZXZlbG9wZWQgYSBzY3JhcHBlciByb2JvdCB0aGF0IGNyYXdscyBhbGwgS2lja3N0YXJ0ZXIgcHJvamVjdHMgbW9udGhseSBzaW5jZSAyMDA5LiBXZSB3aWxsIGp1c3QgdGFrZSBkYXRhIGZyb20gdGhlIG1vc3QgcmVjZW50IGNyYXdsIG9uIDIwMjAtMDItMTMuCgpUbyBzaW1wbGlmeSB5b3VyIHRhc2ssIEkgaGF2ZSBkb3dubG9hZGVkIHRoZSBmaWxlcyBhbmQgcGFydGlhbGx5IGNsZWFuZWQgdGhlIHNjcmFwZWQgZGF0YS4gSW4gcGFydGljdWxhciwgSSBjb252ZXJ0ZWQgc2V2ZXJhbCBKU09OIGNvbHVtbnMsIGNvcnJlY3RlZCBzb21lIG9idmlvdXMgZGF0YSBpc3N1ZXMsIGFuZCByZW1vdmVkIHNvbWUgdmFyaWFibGVzIHRoYXQgYXJlIG5vdCBvZiBpbnRlcmVzdCAob3IgbWlzc2luZyBmcmVxdWVudGx5KSwgYW5kIHJlbW92ZSBzb21lIGR1cGxpY2F0ZWQgcHJvamVjdCBlbnRyaWVzLiBJIGhhdmUgYWxzbyAgc3Vic2V0dGVkIHRoZSBkYXRhIHRvIG9ubHkgY29udGFpbiBwcm9qZWN0cyBvcmlnaW5hdGluZyBpbiB0aGUgVW5pdGVkIFN0YXRlcyAodG8gaGF2ZSBvbmx5IEVuZ2xpc2ggbGFuZ3VhZ2UgYW5kIFVTRCBkZW5vbWluYXRlZCBwcm9qZWN0cykuCgpUaGUgZGF0YSBpcyBjb250YWluZWQgaW4gdGhlIGZpbGUgYGtpY2tzdGFydGVyX3Byb2plY3RzXzIwMjBfMDIuY3N2YCBhbmQgY29udGFpbnMgYWJvdXQgMTI3ayBwcm9qZWN0cyBhbmQgYWJvdXQgMjAgdmFyaWFibGVzLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKYGBge3J9CmtpY2tzdGFydGVyIDwtIHJlYWRfY3N2KCdkYXRhL2tpY2tzdGFydGVyX3Byb2plY3RzXzIwMjBfMDIuY3N2JykKaGVhZChraWNrc3RhcnRlcikKYGBgCgojIyBUYXNrcyBmb3IgdGhlIEFzc2lnbm1lbnQKCiMjIyAxLiBJZGVudGlmeWluZyBTdWNjZXNzZnVsIFByb2plY3RzCgojIyMjIGEpIFN1Y2Nlc3MgYnkgQ2F0ZWdvcnkKClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gaWRlbnRpZnkgc3VjY2VzcyBvZiBhIHByb2plY3Q6ICAKICAtIFN0YXRlIChgc3RhdGVgKTogV2hldGhlciBhIGNhbXBhaWduIHdhcyBzdWNjZXNzZnVsIG9yIG5vdC4gICAKICAtIFBsZWRnZWQgQW1vdW50IChgcGxlZGdlZGApICAgCiAgLSBBY2hpZXZlbWVudCBSYXRpbzogQ3JlYXRlIGEgdmFyaWFibGUgYGFjaGlldmVtZW50X3JhdGlvYCBieSBjYWxjdWxhdGluZyB0aGUgcGVyY2VudGFnZSBvZiB0aGUgb3JpZ2luYWwgbW9uZXRhcnkgYGdvYWxgIHJlYWNoZWQgYnkgdGhlIGFjdHVhbCBhbW91bnQgYHBsZWRnZWRgICh0aGF0IGlzIGBwbGVkZ2VkYFxcYGdvYWxgICoxMDApLiAgICAKICAtIE51bWJlciBvZiBiYWNrZXJzIChgYmFja2Vyc19jb3VudGApICAKICAtIEhvdyBxdWlja2x5IHRoZSBnb2FsIHdhcyByZWFjaGVkIChkaWZmZXJlbmNlIGJldHdlZW4gYGxhdW5jaGVkX2F0YCBhbmQgYHN0YXRlX2NoYW5nZWRfYXRgKSBmb3IgdGhvc2UgY2FtcGFpZ25zIHRoYXQgd2VyZSBzdWNjZXNzZnVsLiAgCgpVc2Ugb25lIG9yIG1vcmUgb2YgdGhlc2UgbWVhc3VyZXMgdG8gdmlzdWFsbHkgc3VtbWFyaXplIHdoaWNoIGNhdGVnb3JpZXMgd2VyZSBtb3N0IHN1Y2Nlc3NmdWwgaW4gYXR0cmFjdGluZyBmdW5kaW5nIG9uIGtpY2tzdGFydGVyLiBCcmllZmx5IHN1bW1hcml6ZSB5b3VyIGZpbmRpbmdzLgpgYGB7cn0KIyBBZGQgdHdvIGNvbHVtbnM6IGFjaGlldmVtZW50X3JhdGlvIGFuZCB0aW1lIGRpZmZlcmVuY2UgYmV0d2VlbiBgbGF1bmNoZWRfYXRgIGFuZCBgc3RhdGVfY2hhbmdlZF9hdGAKa2ljayA8LSBraWNrc3RhcnRlciAlPiUKICBtdXRhdGUoYWNoaWV2ZW1lbnRfcmF0aW8gPSBwbGVkZ2VkL2dvYWwgKiAxMDApICU+JQogIG11dGF0ZShkaWZmX2RheXMgPSBhcy5udW1lcmljKHN0YXRlX2NoYW5nZWRfYXQgLSBsYXVuY2hlZF9hdCkpCmBgYAoKYGBge3J9CmZyZXEgPC0ga2ljayAlPiUKICBncm91cF9ieSh0b3BfY2F0ZWdvcnksc3RhdGUpICU+JQogIHN1bW1hcmlzZShmcmVxID0gbigpKSAlPiUKICBncm91cF9ieSh0b3BfY2F0ZWdvcnkpICU+JQogIG11dGF0ZShwZXJjID0gZnJlcS9zdW0oZnJlcSkqMTAwKQoKZnJlcV9zdWMgPC0gZnJlcSAlPiUKICBmaWx0ZXIoc3RhdGUgPT0gJ3N1Y2Nlc3NmdWwnKSAlPiUKICBzZWxlY3QoLWMoc3RhdGUsZnJlcSksIHBlcmNfc3VjID0gcGVyYykKCmZyZXEgPC0gZnJlcSAlPiUKICBsZWZ0X2pvaW4oZnJlcV9zdWMsIGJ5ID0gJ3RvcF9jYXRlZ29yeScpCmZyZXEkc3RhdGUgPC0gZmFjdG9yKGZyZXEkc3RhdGUsIGxldmVscyA9IGMoJ2ZhaWxlZCcsJ2NhbmNlbGVkJywnc3VzcGVuZGVkJywnbGl2ZScsJ3N1Y2Nlc3NmdWwnKSkKYGBgCjEuIEZpcnN0LCBJIHVzZWQgU3RhdGUgKGBzdGF0ZWApIHRvIGRldGVybWluZSBpZiBvbmUgcHJvamVjdCBpcyBzdWNjZXNzZnVsIG9yIG5vdC4gU28sIEkgY3JlYXRlZCBhIHN0YWNrIGJhciBwbG90IGFuZCBmb3VuZCB0aGF0IGRhbmNlIGFuZCBjb21pY3MgaGF2ZSBoaWdoIHBlcmNlbnRhZ2Ugb2Ygc3VjY2Vzc2Z1bCBwcm9qZWN0cy4KYGBge3J9CmxpYnJhcnkoZ2d0aGVtZXMpCmNhdGVnb3J5X3N0YXRlIDwtIGdncGxvdChmcmVxLCBhZXMoZmlsbCA9IHN0YXRlLCB4ID0gcmVvcmRlcih0b3BfY2F0ZWdvcnkscGVyY19zdWMpLCB5ID0gZnJlcSkpICsKICAgICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuOCkgKwogICAgICAgICBjb29yZF9mbGlwKCkgKwogICAgICAgIHNjYWxlX3hfZGlzY3JldGUoKSArIAogICAgICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkJyQkciKSsKICAgICAgICBsYWJzKHg9IE5VTEwsIHkgPSAiUGVyY2VudGFnZSBvZiBEaWZmZXJlbnQgUHJvamVjdCBTdGF0ZSIsIGZpbGwgPSAiU3RhdGUiKSsKICAgICAgICB0aGVtZV9jbGVhbigpCmNhdGVnb3J5X3N0YXRlCmBgYAoyLiBUaGVuLCBJIGZ1cnRoZXIgZXhwbG9yZSB0aGUgcHJvamVjdHMgYnkgdmlzdWFsaXppbmcgdGhlaXIgYWNoaWV2ZW1lbnQgcmF0aW8uIEFzIHdlIGNhbiBzZWUsIHNvbWUgbXVzaWMgcHJvamVjdHMgaGF2ZSByYXRoZXIgaGlnaCBhY2hpZXZlbWVudCByYXRpby4gQnV0IHNpbmNlIHRoZSBudW1iZXIgb2YgdGhlc2UgbWFpbiBjYXRlZ29yeSBwcm9qZWN0cyBpcyB0b28gbGFyZ2UsIHRoZSB2YXJpYW5jZSBpcyBhbHNvIGhpZ2guCldlIGNhbiBmaW5kIHRoZXJlIGFyZSByZWxhdGl2ZWx5IGZld2VyIGRhbmNlIGFuZCBjb21pY3MgcHJvamVjdHMsIGJ1dCB0aGVzZSBwcm9qZWN0cyByYXJlbHkgaGF2ZSBhIGxvdyBhY2hpZXZlbWVudCByYXRpby4gSSB0aGluayB0aGVzZSBwcm9qZWN0cyBoYXZlIGEgY2VydGFpbiBwcm9mZXNzaW9uYWwgc2tpbGxzIGFuZCB1bmlxdWUgaG9iYmllcywgd2hpY2ggbGltaXQgdGhlIHNjYWxlIG9mIHRoZSBncm91cHMuIEJ1dCBwZW9wbGUgd2hvIHByZWZlciB0aGVzZSBhY3Rpdml0aWVzIGFyZSBtb3JlIHdpbGxpbmcgdG8gcGF5IGZvciB0aGVzZSBwcm9qZWN0cyB0aGFuIHRoZSBtYXNzLgpgYGB7cn0KbGlicmFyeSh2aXJpZGlzKQpjYXRlZ29yeV9hY2hpZXZlX3JhdGlvIDwtIGdncGxvdChraWNrLCBhZXMoeCA9IHRvcF9jYXRlZ29yeSwgeSA9IGFjaGlldmVtZW50X3JhdGlvLCBmaWxsID0gdG9wX2NhdGVnb3J5KSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IHRvcF9jYXRlZ29yeSksc2l6ZSA9IDAuMikrCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhkaXNjcmV0ZT1UUlVFLCBvcHRpb249InZpcmlkaXMiKSsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAnbG9nMTAnKSsKICBnZ3RpdGxlKCJBY2hpZXZlbWVudCBSYXRpbyBvZiBEaWZmZXJlbnQgQ2F0ZWdvcnkgUHJvamVjdHMiKSsKICBsYWJzKHg9ICJDYXRlZ29yeSIsIHkgPSAiQWNoaWV2ZW1lbnQgUmF0aW8iKSArCiAgdGhlbWVfY2xlYW4oKSsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpCiAgICAgICAgKQoKY2F0ZWdvcnlfYWNoaWV2ZV9yYXRpbwpgYGAKCiMjIyMgKipCT05VUyBPTkxZOioqIGIpIFN1Y2Nlc3MgYnkgTG9jYXRpb24KCk5vdywgdXNlIHRoZSBsb2NhdGlvbiBpbmZvcm1hdGlvbiB0byBjYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBzdWNjZXNzZnVsIHByb2plY3RzIGJ5IHN0YXRlIChpZiB5b3UgYXJlIGFtYml0aW91cywgbm9ybWFsaXplIGJ5IHBvcHVsYXRpb24pLiBBbHNvLCBpZGVudGlmeSB0aGUgVG9wIDUwICJpbm5vdmF0aXZlIiBjaXRpZXMgaW4gdGhlIFUuUy4gKGJ5IHdoYXRldmVyIG1lYXN1cmUgeW91IGZpbmQgcGxhdXNpYmxlKS4gUHJvdmlkZSBhIGxlYWZsZXQgbWFwIHNob3dpbmcgdGhlIG1vc3QgaW5ub3ZhdGl2ZSBzdGF0ZXMgYW5kIGNpdGllcyBpbiB0aGUgVS5TLiBvbiBhIHNpbmdsZSBtYXAgYmFzZWQgb24gdGhlc2UgaW5mb3JtYXRpb24uCgpgYGB7cn0KbGlicmFyeShvcGVuaW50cm8pCmxpYnJhcnkoYWxiZXJzdXNhKQpsaWJyYXJ5KHNwKQojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHN1Y2Nlc3NmdWwgcHJvamVjdHMgYnkgc3RhdGUKc3RhdGVfc3VjIDwtIGtpY2sgJT4lCiAgZmlsdGVyKHN0YXRlID09ICJzdWNjZXNzZnVsIikgJT4lCiAgZ3JvdXBfYnkobG9jYXRpb25fc3RhdGUpICU+JQogIHN1bW1hcmlzZShudW0gPSBuKCkpICU+JQogIG11dGF0ZShzdGF0ZV9uYW1lID0gYWJicjJzdGF0ZShsb2NhdGlvbl9zdGF0ZSkpIAoKIyBVc2UgdXNhX2RmKCkKc3RhdGVfaW5ubyA8LSB1c2Ffc2YoKSAlPiUKICBsZWZ0X2pvaW4oc3RhdGVfc3VjLCBjKCJuYW1lIj0ic3RhdGVfbmFtZSIpKQoKIyBOb3JtYWxpemUgYnkgcG9wdWxhdGlvbgpzdGF0ZV9pbm5vX3BvcCA8LSBzdGF0ZV9pbm5vICU+JQogIG11dGF0ZShpbm5vX3BvcCA9IG51bS9wb3BfMjAxNCoxMDAwMCkKCm1vc3RfaW5ub19jaXR5IDwtIGtpY2sgJT4lCiAgZ3JvdXBfYnkobG9jYXRpb25fdG93biklPiUKICBzdW1tYXJpc2UoYXZnX2FjaGlldmVfcmF0aW8gPSByb3VuZChtZWRpYW4oYWNoaWV2ZW1lbnRfcmF0aW8pLDIpKSAlPiUKICBhcnJhbmdlKGRlc2MoYXZnX2FjaGlldmVfcmF0aW8pKSU+JQogIGhlYWQoNTApCm1vc3RfaW5ub19jaXR5W21vc3RfaW5ub19jaXR5JGxvY2F0aW9uX3Rvd249PSAiMzQ3NzMiLF0kbG9jYXRpb25fdG93biA8LSAiU2FpbnQgQ2xvdWQiCm1vc3RfaW5ub19jaXR5W21vc3RfaW5ub19jaXR5JGxvY2F0aW9uX3Rvd249PSAiMzI4MjEiLF0kbG9jYXRpb25fdG93biA8LSAiT3JsYW5kbyIKbW9zdF9pbm5vX2NpdHlbbW9zdF9pbm5vX2NpdHkkbG9jYXRpb25fdG93bj09ICIxOTEzNiIsXSRsb2NhdGlvbl90b3duIDwtICJQaGlsYWRlbHBoaWEiCm1vc3RfaW5ub19jaXR5W21vc3RfaW5ub19jaXR5JGxvY2F0aW9uX3Rvd249PSAiMTMzNjEiLF0kbG9jYXRpb25fdG93biA8LSAiSm9yZGFudmlsbGUiCm1vc3RfaW5ub19jaXR5W21vc3RfaW5ub19jaXR5JGxvY2F0aW9uX3Rvd249PSAiV3lud29vZCBBcnQgRGlzdHJpY3QiLF0kbG9jYXRpb25fdG93biA8LSAiV3lud29vZCIKIyBNb3N0IGlubm92YXRpdmUgNTAgY2l0aWVzIGluIHRoZSBVbml0ZWQgU3RhdGVzCmNpdGllcyA8LSBtb3N0X2lubm9fY2l0eSRsb2NhdGlvbl90b3duCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2VvbmFtZXMpCm9wdGlvbnMoZ2VvbmFtZXNVc2VybmFtZT0ibGlsaXh1ZXlpbmciKQpvcHRpb25zKGdlb25hbWVzSG9zdD0iYXBpLmdlb25hbWVzLm9yZyIpCgpHTnNlYXJjaFVTIDwtIGZ1bmN0aW9uKHgpewogIGNpdHlfZGYgPC0gR05zZWFyY2gobmFtZSA9IHgsIGNvdW50cnkgPSAiVVMiKQogIGNpdHlfY29vcmRzIDwtIGNpdHlfZGZbMSwgYygibG5nIiwgImxhdCIpXQogIHJldHVybihjaXR5X2Nvb3JkcykKfQpHTnJlc3VsdDwtIGxhcHBseShjaXRpZXMsIEdOc2VhcmNoVVMpIApgYGAKYGBge3J9CnJlc3VsdCA8LSBkby5jYWxsKCJyYmluZCIsIEdOcmVzdWx0KQp0b3BfaW5ub19jaXRpZXMgPC0gY2JpbmQobW9zdF9pbm5vX2NpdHksIHJlc3VsdCkKIyBQcmVwYXJlIHRoZSBjaXRpZXMgZGF0YXNldCBhbmQgcHJvdmlkZSB0aGUgbG9uL2xhdCBmb3IgY2l0aWVzCnRvcF9pbm5vX2NpdGllcyRsbmcgPC0gYXMubnVtZXJpYyh0b3BfaW5ub19jaXRpZXMkbG5nKQp0b3BfaW5ub19jaXRpZXMkbGF0IDwtIGFzLm51bWVyaWModG9wX2lubm9fY2l0aWVzJGxhdCkKaGVhZCh0b3BfaW5ub19jaXRpZXMpCmBgYApUaGUgY29sb3Igb2YgZWFjaCBzdGF0ZSByZXByZXNlbnRzIHRoZSBhdmVyYWdlIG51bWJlciBvZiBzdWNjZXNzZnVsIHByb2plY3RzIChQZXIgMTAwMDAgUG9wdWxhdGlvbikgaW4gdGhpcyBzdGF0ZS4gQW5kIEkgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGFjaGlldmVtZW50IHJhdGlvIGZvciBlYWNoIHRvd24gYW5kIHN1YnNldCB0aGUgdG9wIDUwIGlubm92YXRpdmUgY2l0aWVzIHRvIHB1dCBvbiB0aGUgbWFwLiBDbGlja2luZyB0aGUgcG9wdXAgY2FuIGFjY2VzcyBtb3JlIGRldGFpbHMuCmBgYHtyfQpsaWJyYXJ5KGxlYWZsZXQpCnNwZGYgPC0gcm1hcHNoYXBlcjo6bXNfc2ltcGxpZnkoc3RhdGVfaW5ub19wb3AsIGtlZXAgPSAwLjEpCnBhbF9yZXNwb25zZSA8LSBjb2xvck51bWVyaWMoIkduQnUiLGRvbWFpbiA9IDA6NykKCmVwc2cyMTYzIDwtIGxlYWZsZXRDUlMoCiAgY3JzQ2xhc3MgPSAiTC5Qcm9qLkNSUyIsCiAgY29kZSA9ICJFUFNHOjIxNjMiLAogIHByb2o0ZGVmID0gIitwcm9qPWxhZWEgK2xhdF8wPTQ1ICtsb25fMD0tMTAwICt4XzA9MCAreV8wPTAgK2E9NjM3MDk5NyArYj02MzcwOTk3ICt1bml0cz1tICtub19kZWZzIiwKICByZXNvbHV0aW9ucyA9IDJeKDE2OjcpKQoKaW5ub19pY29ucyA8LSBpY29ucygKICBpY29uVXJsID0gImRhdGEvaW5ub19pY29uLnBuZyIsCiAgaWNvbldpZHRoID0gMjAsIGljb25IZWlnaHQgPSAyMCwKICBpY29uQW5jaG9yWCA9IDcuNSwgaWNvbkFuY2hvclkgPSA4LjUKICApCgpzcGRmICU+JQogIGxlYWZsZXQob3B0aW9ucyA9IGxlYWZsZXRPcHRpb25zKGNycyA9IGVwc2cyMTYzKSkgJT4lCiAgYWRkUG9seWdvbnMod2VpZ2h0ID0gMSwgb3BhY2l0eSA9IDEsIGNvbG9yID0gIiM0NDQ0NDQiLCAKICAgIGZpbGxDb2xvciA9IH5wYWxfcmVzcG9uc2UoaW5ub19wb3ApLCBmaWxsT3BhY2l0eSA9IDAuNywgc21vb3RoRmFjdG9yID0gMC41LAogICAgbGFiZWwgPSB+cGFzdGUobmFtZSwgIjogIixpbm5vX3BvcCwgInN1Y2Nlc3NmdWwgUHJvamVjdHMgKFBlciAxMGsgUG9wdWxhdGlvbikiKSklPiUKICAgIGFkZE1hcmtlcnMoZGF0YSA9IHRvcF9pbm5vX2NpdGllcywgbG5nID0gfmxuZywgbGF0ID0gfmxhdCwgcG9wdXAgPSB+cGFzdGUoIlRvcCA1MCBJbm5vdmF0aXZlIFRvd246ICIsIGxvY2F0aW9uX3Rvd24sICI8YnI+QXZlcmFnZSBBY2hpZXZlbWVudCBSYXRpbzogIiwgYXZnX2FjaGlldmVfcmF0aW8pLCBpY29uID0gaW5ub19pY29ucyklPiUKICAgIGFkZExlZ2VuZChwYWwgPSBwYWxfcmVzcG9uc2UsIHZhbHVlcyA9IDA6NywgdGl0bGUgPSBOVUxMLAogICAgICAgICAgICBwb3NpdGlvbiA9ICJ0b3BsZWZ0Iiwgb3BhY2l0eT0wLjcpCmBgYAoKCgojIyMgMi4gV3JpdGluZyB5b3VyIHN1Y2Nlc3Mgc3RvcnkKCkVhY2ggcHJvamVjdCBjb250YWlucyBhIGBibHVyYmAgLS0gYSBzaG9ydCBkZXNjcmlwdGlvbiBvZiB0aGUgcHJvamVjdC4gV2hpbGUgbm90IHRoZSBmdWxsIGRlc2NyaXB0aW9uIG9mIHRoZSBwcm9qZWN0LCB0aGUgc2hvcnQgaGVhZGxpbmUgaXMgYXJndWFibHkgaW1wb3J0YW50IGZvciBpbmR1Y2luZyBpbnRlcmVzdCBpbiB0aGUgcHJvamVjdCAoYW5kIHVsdGltYXRlbHkgcG9wdWxhcml0eSBhbmQgc3VjY2VzcykuIExldCdzIGFuYWx5emUgdGhlIHRleHQuCgojIyMjIGEpIENsZWFuaW5nIHRoZSBUZXh0IGFuZCBXb3JkIENsb3VkCmBgYHtyfQpsaWJyYXJ5KHRtKSAgICAgICAjIEZyYW1ld29yayBmb3IgdGV4dCBtaW5pbmcuCmxpYnJhcnkocXVhbnRlZGEpICMgQW5vdGhlciBncmVhdCB0ZXh0IG1pbmluZyBwYWNrYWdlCmxpYnJhcnkodGlkeXRleHQpICMgVGV4dCBhcyBUaWR5IERhdGEgKGdvb2QgZm9yIHVzZSB3aXRoIGdncGxvdDIpCmxpYnJhcnkocWRhcCkKbGlicmFyeShTbm93YmFsbEMpCmBgYAoKMSkgVG8gcmVkdWNlIHRoZSB0aW1lIGZvciBhbmFseXNpcywgc2VsZWN0IHRoZSAxMDAwIG1vc3Qgc3VjY2Vzc2Z1bCBwcm9qZWN0cyBhbmQgYSBzYW1wbGUgb2YgMTAwMCB1bnN1Y2Nlc3NmdWwgcHJvamVjdHMuIApgYGB7cn0KIyBGaWx0ZXIgdGhvc2Ugc3VjY2Vzc2Z1bCBwcm9qZWN0cyBhbmQgcGljayB0aG9zZSB3aXRoIGhpZ2hlciBhY2hpZXZlbWVudCByYXRpbwpzdWNjZXNzZnVsIDwtIGtpY2sgJT4lCiAgZmlsdGVyKHN0YXRlID09ICJzdWNjZXNzZnVsIikgJT4lCiAgYXJyYW5nZShkZXNjKGFjaGlldmVtZW50X3JhdGlvKSkgJT4lCiAgaGVhZCgxMDAwKQojIEZpbHRlciB0aG9zZSBmYWlsZWQgcHJvamVjdHMgd2l0aCBsb3dlciBhY2hpZXZlbWVudCByYXRpbwpmYWlsIDwtIGtpY2sgJT4lCiAgZmlsdGVyKHN0YXRlID09ICJmYWlsZWQiKSAlPiUKICBhcnJhbmdlKGFjaGlldmVtZW50X3JhdGlvKSAlPiUKICBoZWFkKDEwMDApCmFsbCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHN1Y2Nlc3NmdWwkYmx1cmIsZmFpbCRibHVyYiksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKY29sbmFtZXMoYWxsKSA8LSBjKCJzdWNjZXNzIiwiZmFpbCIpCmhlYWQoYWxsKQpgYGAKCjIpIFVzZSB0aGUgY2xlYW5pbmcgZnVuY3Rpb25zIHRvIHJlbW92ZSB1bm5lY2Vzc2FyeSB3b3JkcyAoc3RvcCB3b3JkcyksIHN5bnRheCwgcHVuY3R1YXRpb24sIG51bWJlcnMsIHdoaXRlIHNwYWNlIGV0Yy4gTm90ZSwgdGhhdCBtYW55IHByb2plY3RzIHVzZSB0aGVpciBvd24gdW5pcXVlIGJyYW5kIG5hbWVzIGluIHVwcGVyIGNhc2VzLCBzbyB0cnkgdG8gcmVtb3ZlIHRoZXNlIGZ1bGx5IGNhcGl0YWxpemVkIHdvcmRzIGFzIHdlbGwgKHNpbmNlIHdlIGFyZSBhaW1pbmcgdG8gaWRlbnRpZnkgY29tbW9uIHdvcmRzIGFjcm9zcyBkZXNjcmlwdGlvbnMpLiAKCmBgYHtyfQojIERlZGluZSBhIGNsZWFuX2NvcnB1cyBmdW5jdGlvbgpjbGVhbl9jb3JwdXMgPC0gZnVuY3Rpb24oY29ycHVzKXsKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcihmdW5jdGlvbih4KXsKICAgICAgICAgICAgICBwcm9jZXNzZWQgPC0gZ3N1YihwYXR0ZXJuID0gJ1teYS16QS1aMC05XFxzXSsnLCB4LCByZXBsYWNlbWVudCA9ICIiLAogICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVFJVRSxwZXJsID0gVFJVRSkKICAgICAgICAgICAgICAjIFJlbW92ZSBmdWxseSBjYXBpdGFsaXplZCB3b3Jkcy4gIlxiIiBtZWFucyB3b3JkIGJvdW5kYXJ5LiAKICAgICAgICAgICAgICBwcm9jZXNzZWQgPC0gZ3N1YigiXFxiW0EtWl0rXFxiIiwiIiwgcHJvY2Vzc2VkKQogICAgICAgICAgICAgIHJldHVybihwcm9jZXNzZWQpfSkpCiAgIyBjb252ZXJ0IHVwcGVyIGNhc2UgdG8gbG93ZXIgY2FzZSwgcmVtb3ZlIHN5bWJvbHMgYW5kICguLikKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcih0b2xvd2VyKSkKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcihyZXBsYWNlX3N5bWJvbCkpCiAgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIGNvbnRlbnRfdHJhbnNmb3JtZXIocmVwbGFjZV9jb250cmFjdGlvbikpCiAgIyByZW1vdmUgbnVtYmVycywgcHVjdHVhdGlvbnMgYW5kIHdoaXRlc3BhY2UKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlTnVtYmVycykKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlUHVuY3R1YXRpb24pCiAgY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHN0cmlwV2hpdGVzcGFjZSkKICAjIFJlbW92ZSBzdG9wd29yZHMKICBjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsYyhzdG9wd29yZHMoImVuIiksImtpY2tzdGFydGVyIiwgInByb2plY3QiLCAicmQiLCAic3QiLCAidGgiLCJ0aGUiKSkKICAKICByZXR1cm4oY29ycHVzKQp9CmBgYAoKCmBgYHtyfQojIENvbnZlcnQgZGVzY3JpcHRpb25zIG9mIHN1Y2Nlc3NmdWwgcHJvamVjdHMgdG8gY29ycHVzCnNfcHJvamVjdHMgPC0gVmVjdG9yU291cmNlKGFsbCRzdWNjZXNzKQpzX2NvcnB1cyA8LSBWQ29ycHVzKHNfcHJvamVjdHMpCnNfY2xlYW4gPC0gY2xlYW5fY29ycHVzKHNfY29ycHVzKQpgYGAKCmBgYHtyfQojIENvbnZlcnQgZGVzY3JpcHRpb25zIG9mIGZhaWxlZCBwcm9qZWN0cyB0byBjb3JwdXMKZl9wcm9qZWN0cyA8LSBWZWN0b3JTb3VyY2UoYWxsJGZhaWwpCmZfY29ycHVzIDwtIFZDb3JwdXMoZl9wcm9qZWN0cykKZl9jbGVhbiA8LSBjbGVhbl9jb3JwdXMoZl9jb3JwdXMpCmBgYAoKMykgU3RlbSB0aGUgd29yZHMgbGVmdCBvdmVyIGFuZCBjb21wbGV0ZSB0aGUgc3RlbXMuIApgYGB7cn0Kc19zdGVtZWQgPC0gdG1fbWFwKHNfY2xlYW4sIHN0ZW1Eb2N1bWVudCkKZl9zdGVtZWQgPC0gdG1fbWFwKGZfY2xlYW4sIHN0ZW1Eb2N1bWVudCkKYGBgCgpgYGB7cn0KIyBTdGVtIGNvbXBsZXRpb24gZnVuY3Rpb24Kc3RlbUNvbXBsZXRpb24yIDwtIGZ1bmN0aW9uKHgsIGRpY3Rpb25hcnkpIHsKICAgeCA8LSB1bmxpc3Qoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHgpLCAiICIpKQogICAjIHN0ZW1Db21wbGV0aW9uIGNvbXBsZXRlcyBhbiBlbXB0eSBzdHJpbmcgdG8gYSB3b3JkIGluIGRpY3Rpb25hcnkuIAogICAjIFJlbW92ZSBlbXB0eSBzdHJpbmcgdG8gYXZvaWQgaXNzdWUuCiAgIHggPC0geFt4ICE9ICIiXQogICB4IDwtIHN0ZW1Db21wbGV0aW9uKHgsIGRpY3Rpb25hcnk9ZGljdGlvbmFyeSkKICAgeCA8LSBwYXN0ZSh4LCBzZXA9IiIsIGNvbGxhcHNlPSIgIikKICAgUGxhaW5UZXh0RG9jdW1lbnQoc3RyaXBXaGl0ZXNwYWNlKHgpKQp9CmBgYAoKYGBge3J9CiMgU3RlbSBjb21wbGV0aW9uCnNfY29tcCA8LSBsYXBwbHkoc19zdGVtZWQsIHN0ZW1Db21wbGV0aW9uMiwgCiAgICAgICAgICAgICAgICAgICAgIGRpY3Rpb25hcnk9c19jbGVhbikKc19jb21wX2NvcnB1cyA8LSBhcy5WQ29ycHVzKHNfY29tcCkKZl9jb21wIDwtIGxhcHBseShmX3N0ZW1lZCwgc3RlbUNvbXBsZXRpb24yLAogICAgICAgICAgICAgICAgIGRpY3Rpb25hcnkgPWZfY2xlYW4pCmZfY29tcF9jb3JwdXMgPC0gYXMuVkNvcnB1cyhmX2NvbXApCmBgYAoKYGBge3J9CiMgQ29tcGFyZSB0aGUgcmVzdWx0IG9mIHRleHQgc3RlbW1lZCBhbmQgc3RlbSBjb21wbGV0ZWQKcHJpbnQoc19jb21wX2NvcnB1c1tbMV1dJGNvbnRlbnQpCnByaW50KHNfc3RlbWVkW1sxXV0kY29udGVudCkKcHJpbnQoZl9jb21wX2NvcnB1c1tbMTVdXSRjb250ZW50KQpwcmludChmX3N0ZW1lZFtbMTVdXSRjb250ZW50KQpgYGAKCjQpIENyZWF0ZSBhIGRvY3VtZW50LXRlcm0tbWF0cml4LgpgYGB7cn0Kc19kdG0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KHNfY29tcF9jb3JwdXMpCmZfZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChmX2NvbXBfY29ycHVzKQpwcmludChzX2R0bSkKcHJpbnQoZl9kdG0pCmBgYAoKNSkgUHJvdmlkZSBhIHdvcmQgY2xvdWQgb2YgdGhlIG1vc3QgZnJlcXVlbnQgb3IgaW1wb3J0YW50IHdvcmRzIGFtb25nIHRoZSBtb3N0IHN1Y2Nlc3NmdWwgcHJvamVjdHMuCgpJIGNob3NlIFRGSURGIGFzIHRoZSB3ZWlnaHQgbWVhc3VyZSBhbmQgZm91bmQgdGhlIG1vc3QgZnJlcXVlbnQgd29yZHMgKHBlbmFsaXplZCBieSBmcmVxdWVuY3kgaW4gYWxsIGRvY3VtZW50cykuIEFzIHdlIGNhbiBzZWUgaW4gdGhpcyB3b3JkY2xvdWQsIGRlc2lnbiwgZ2FtZSwgZW5hbWVsLCBhcnQsIGFsYnVtLCBjb21pYywgcGxheSBhcmUgbW9zdCBmcmVxdWVudCB0aGVtZSBtZW50aW9uZWQgaW4gc3VjY2Vzc2Z1bCBwcm9qZWN0cydkZXNjcmlwdGlvbnMuIEJlc2lkZXMsIHRob3NlIHByb2plY3RzIHdpdGggY3JlYXRpdmUgaWRlYSBhcmUgbW9yZSBsaWtlbHkgdG8gYmUgc3VjY2Vzc2Z1bC4gRm9yIGV4YW1wbGUsIHdlIHNlZSBzb21lIHdvcmRzIGxpa2UgbmV3LCB0aW1lc3QsIGNyZWF0ZSwgZmlyc3QsIGVkaXRpb24gZXRjLiBUaGVzZSB3b3JkcyBpbmRpY2F0ZSB0aGF0IHN1Y2Nlc3NmdWwgcHJvamVjdHMgY29udmV5IGEga2luZCBvZiBuZXcgZXhwZXJpZW5jZSB0byBwZW9wbGUuCmBgYHtyfQojIEkgY2hvb3NlIFRGSURGIHRvIHdlaWdodCB3b3Jkcwp0ZmlkZl9kdG0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KHNfY29tcF9jb3JwdXMsIGNvbnRyb2wgPSBsaXN0KHdlaWdodCA9IHdlaWdodFRmSWRmKSkKdGZpZGZfZHRtX20gPC0gYXMubWF0cml4KHRmaWRmX2R0bSkKdGZpZGZfd2VpZ2h0IDwtIGNvbFN1bXModGZpZGZfZHRtX20pCnRmaWRmX3dvcmQgPC0gbmFtZXModGZpZGZfd2VpZ2h0KQpgYGAKYGBge3J9CmxpYnJhcnkod29yZGNsb3VkKQpwYWwyIDwtIGJyZXdlci5wYWwoOCwiRGFyazIiKQpzZXQuc2VlZCgxKQp3b3JkY2xvdWQodGZpZGZfd29yZCwgdGZpZGZfd2VpZ2h0LCAKICAgICAgICAgIG1pbi5mcmVxPTEsCiAgICAgICAgICBtYXgud29yZHM9NTAsCiAgICAgICAgICBjb2xvcnM9YygiRGFya09yYW5nZSIsICJDb3JuZmxvd2VyQmx1ZSIsICJEYXJrUmVkIiksCiAgICAgICAgICByb3QucGVyID0gMC4zLAogICAgICAgICAgcmFuZG9tLm9yZGVyPUZBTFNFLAogICAgICAgICAgcmFuZG9tLmNvbG9yPUZBTFNFKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHdvcmRjbG91ZDIpCmRmX3RmaWRmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQodGZpZGZfd29yZCx0ZmlkZl93ZWlnaHQpLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsICkKZGZfdGZpZGYkdGZpZGZfd2VpZ2h0IDwtIGFzLmludGVnZXIoZGZfdGZpZGYkdGZpZGZfd2VpZ2h0KQp3b3JkY2xvdWQyKGRmX3RmaWRmLCBzaXplPTEuNiwgY29sb3I9J3JhbmRvbS1kYXJrJykKYGBgCgojIyMjIGIpIFN1Y2Nlc3MgaW4gd29yZHMKClByb3ZpZGUgYSBweXJhbWlkIHBsb3QgdG8gc2hvdyBob3cgdGhlIHdvcmRzIGJldHdlZW4gc3VjY2Vzc2Z1bCBhbmQgdW5zdWNjZXNzZnVsIHByb2plY3RzIGRpZmZlciBpbiBmcmVxdWVuY3kuIEEgc2VsZWN0aW9uIG9mIDEwIC0gMjAgdG9wIHdvcmRzIGlzIHN1ZmZpY2llbnQgaGVyZS4gCmBgYHtyfQojIGNvbWJpbmUgZGVzY3JpcHRpb25zIG9mIHN1Y2Nlc3NmdWwgcHJvamVjdHMgYW5kIGZhaWxlZCBwcm9qZWN0cwphbGxfcyA8LSBwYXN0ZShzdWNjZXNzZnVsJGJsdXJiLCBjb2xsYXBzZSA9ICIiKQphbGxfZiA8LSBwYXN0ZShmYWlsJGJsdXJiLCBjb2xsYXBzZSA9ICIiKQphbGxfcHJvamVjdHMgPC1jKGFsbF9zLCBhbGxfZikKYWxsX2NvcnB1cyA8LSBWQ29ycHVzKFZlY3RvclNvdXJjZShhbGxfcHJvamVjdHMpKQphbGxfY2xlYW4gPC0gY2xlYW5fY29ycHVzKGFsbF9jb3JwdXMpCmFsbF90ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGFsbF9jbGVhbikKYGBgCgoxKSBGaXJzdCwgSSBtYWRlIGEgcG9sYXJpemVkIHdvcmRjbG91ZCBmb3Igc3VjY2Vzc2Z1bCBwcm9qZWN0cyBhbmQgZmFpbGVkIHByb2plY3RzLiBDb21wYXJlZCB0byBzdWNjZXNzZnVsIHByb2plY3RzLCB0aGUgbW9zdCBjb21tb24gYnV0IHVuaXF1ZSB3b3JkcyBmb3IgZmFpbGVkIHByb2plY3RzIGluY2x1ZGUgY29tbXVuaXR5LCBmYW1pbHksIGxvY2FsLCBzaGFyZSwgcGVvcGxlLCB3aGljaCBpbmRpY2F0ZXMgYSBraW5kIG9mIGNvbGxlY3RpdmlzbSBhbmQgdHJhZGl0aW9uIGxvY2FsIHN0eWxlLiBPbiB0aGUgb3RoZXIgaGFuZCwgc3VjY2Vzc2Z1bCBwcm9qZWN0cyB1bmRlcnNjb3JlIGlubm92YXRpb24gYW5kIGluZGl2aWR1YXRpb24uCmBgYHtyfQojIFBvbGFyaXplZCB0YWcgY2xvdWQKY29sbmFtZXMoYWxsX3RkbSkgPC0gYygic3VjY2VzcyIsImZhaWwiKQphbGxfbSA8LSBhcy5tYXRyaXgoYWxsX3RkbSkKY29tcGFyaXNvbi5jbG91ZChhbGxfbSwgY29sb3JzID0gYygiY3lhbjQiLCAiZGFya2dvbGRlbnJvZDIiKSwgbWF4LndvcmRzID0gNDApCmBgYAoKMikgVGhlbiwgSSBqdXN0IGtlcHQgdGhvc2UgY29tbW9uIHdvcmRzIGluIGJvdGggc3VjY2Vzc2Z1bCBhbmQgZmFpbGVkIHByb2plY3RzLCBjYWxjdWxhdGVkIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGZyZXF1ZW5jeSBhbmQgc2hvd2VkIHRob3NlIGNvbW1vbiB3b3JkcyB3aXRoIGdyZWF0ZXN0IGRpZmZlcmVuY2UuIEFjY29yZGluZyB0byB0aGUgcHlyYW1pZCBwbG90LCB3ZSBjYW4gZm91bmQgc3VjY2Vzc2Z1bCBwcm9qZWN0cyBkZXNjcmliZSBtb3JlIG9uIHRoZSBwcm9qZWN0cyB0aGVtc2VsdmVzLCBsaWtlIGdhbWUsIGNvbWljLGVkaXRpb24sIGNhcmRzLiBIb3dldmVyLCBmYWlsZWQgcHJvamVjdHMgdHJ5IHRvIGNvbnZleSB0aGUgbWVhbmluZyBvZiB0aGUgcHJvamVjdHMsIHNvIHRoZXJlIGFyZSBhIGxvdCBvZiB3b3JkcyBsaWtlIGNhbiwgaGVscCwgd29ybGQsIGNyZWF0ZSBldGMuCmBgYHtyfQpsaWJyYXJ5KHBsb3RyaXgpCmFsbF9tX2RmIDwtIGFzLmRhdGEuZnJhbWUoYWxsX20pCmFsbF9tX2RmJHRlcm1zIDwtIHJvd25hbWVzKGFsbF9tX2RmKSAKY29tbW9uX3dvcmRzIDwtIGFsbF9tX2RmICU+JQogIGZpbHRlcihzdWNjZXNzIT0wLCBmYWlsIT0wKSAlPiUKICBtdXRhdGUoZGlmZiA9IGFicyhzdWNjZXNzIC0gZmFpbCkpCnRvcF9kZiA8LSB0b3Bfbihjb21tb25fd29yZHMsIDIwLCBkaWZmKQpweXJhbWlkLnBsb3QodG9wX2RmJHN1Y2Nlc3MsIHRvcF9kZiRmYWlsLCBsYWJlbHMgPSB0b3BfZGYkdGVybXMsIGdhcCA9IDEwLAogICAgICAgICAgICAgdG9wLmxhYmVscyA9IGMoIlN1Y2Nlc3MgUHJvamVjdHMiLCAiV29yZHMiLCAiRmFpbCBQcm9qZWN0cyIpLAogICAgICAgICAgICAgbWFpbiA9ICJXb3JkcyBpbiBDb21tb24iLCB1bml0ID0gTlVMTCwgbGFiZWxjZXg9MC43KQpgYGAKCiMjIyMgYykgU2ltcGxpY2l0eSBhcyBhIHZpcnR1ZQoKVGhlc2UgYmx1cmJzIGFyZSBzaG9ydCBpbiBsZW5ndGggKG1heC4gMTUwIGNoYXJhY3RlcnMpIGJ1dCBsZXQncyBzZWUgd2hldGhlciBicmV2aXR5IGFuZCBzaW1wbGljaXR5IHN0aWxsIG1hdHRlcnMuIENhbGN1bGF0ZSBhIHJlYWRhYmlsaXR5IG1lYXN1cmUgKEZsZXNoIFJlYWRpbmcgRWFzZSwgRmxlc2ggS2luY2FpZCBvciBhbnkgb3RoZXIgY29tcGFyYWJsZSBtZWFzdXJlKSBmb3IgdGhlIHRleHRzLiBWaXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSByZWFkYWJpbGl0eSBtZWFzdXJlIGFuZCBvbmUgb2YgdGhlIG1lYXN1cmVzIG9mIHN1Y2Nlc3MuIEJyaWVmbHkgY29tbWVudCBvbiB5b3VyIGZpbmRpbmcuCgoxKSBJIGNhbGN1bGF0ZSBGbGVzaCwgRmxlc2ggS2luY2FpZCBtZWFzdXJlcy4gQ29tcGFyZSB0aGUgZWZmZWN0cyB0aGV5IHNob3dlZCBpbiB2aXN1YWxpemF0aW9uLCBJIGV2ZW50dWFsbHkgY2hvc2UgdG8gdXNlIEZsZXNoIEtpbmNhaWQgYXMgdGhlIGluZGV4IG9mIHJlYWRhYmlsaXR5LiBUaGUgbG93ZXIgdGhlIEZsZXNjaC1LaW5jYWlkIEdyYWRlIExldmVsLCB0aGUgZWFzaWVyIGEgcGllY2Ugb2YgdGV4dCBpcyB0byByZWFkLgpgYGB7cn0KbGlicmFyeShxdWFudGVkYSkKcmVhZF9zIDwtIHN1Y2Nlc3NmdWwlPiUKICBzZWxlY3QoaWQsIGJsdXJiLCBzdGF0ZSwgdG9wX2NhdGVnb3J5LCBhY2hpZXZlbWVudF9yYXRpbywgZGlmZl9kYXlzLCBiYWNrZXJzX2NvdW50KQpyZWFkX2YgPC0gZmFpbCU+JQogICBzZWxlY3QoaWQsIGJsdXJiLCBzdGF0ZSwgdG9wX2NhdGVnb3J5LCBhY2hpZXZlbWVudF9yYXRpbywgZGlmZl9kYXlzLCBiYWNrZXJzX2NvdW50KQpyZWFkX2FsbCA8LSByYmluZChyZWFkX3MscmVhZF9mKQpkZl9yZWFkIDwtIGRhdGEuZnJhbWUoZG9jX2lkID0gcmVhZF9hbGwkaWQsIHRleHQgPSByZWFkX2FsbCRibHVyYiwgcmVhZF9hbGxbLDM6N10sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZGZfc291cmNlIDwtIERhdGFmcmFtZVNvdXJjZShkZl9yZWFkKQpkZl9jb3JwdXMgPC0gVkNvcnB1cyhkZl9zb3VyY2UpCnJlYWRfY29ycHVzIDwtIGNvcnB1cyhkZl9jb3JwdXMpCkZSRV9yZWFkIDwtIHRleHRzdGF0X3JlYWRhYmlsaXR5KHJlYWRfY29ycHVzLAogICAgICAgICAgICAgIG1lYXN1cmU9YygnRmxlc2NoJywgJ0ZsZXNjaC5LaW5jYWlkJykpCnJlYWRfc2NvcmUgPC0gZGF0YS5mcmFtZShjYmluZChyZWFkX2FsbCxGUkVfcmVhZCkpCnJlYWRfc2NvcmUgPC1yZW5hbWUocmVhZF9zY29yZSwgYygiRksiID0gIkZsZXNjaC5LaW5jYWlkIikpCmhlYWQocmVhZF9zY29yZSkKYGBgCjIpIEluIHRoaXMgcGxvdCwgSSB0cnkgdG8gZmluZCB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSByZWFkYWJpbGl0eSBtZWFzdXJlIGFuZCBzdWNjZXNzZnVsIHByb2plY3RzLiBGcm9tIHRoaXMgcGxvdCwgd2UgY2FuIG5vdCBpbmNsdWRlIHRoYXQgcmVhZGFiaWxpdHkgaGFzIGRpcmVjdCByZWxhdGlvbnMgd2l0aCBzdWNjZXNzZnVsIG9yIGZhaWxlZCBzdGF0ZS4gSG93ZXZlciwgd2UgY2FuIHN0aWxsIGdldCBzb21lIHVzZWZ1bCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBwbG90LiBUaGVyZSBhcmUgdHdvIGtleSB0aW1lLCBhYm91dCAzMCBkYXlzIGFuZCA2MCBkYXlzLiBUaGlzIG1lYW5zIHRoZSB0aW1lIGZvciBwcm9qZWN0cyB0byBjb2xsZWN0IGNyb3dkIGZ1bmRpbmcgaXMgYWJvdXQgb25lIG1vbnRoIGFuZCB0d28gbW9udGhzLiBBbmQgd2UgY2FuIGZpbmQgc3VjY2Vzc2Z1bCBwcm9qZWN0cyBoYXZlIG1vcmUgYmFja2VycyB0aGFuIHRoYXQgb2YgZmFpbGVkLgpgYGB7cn0KcmVhZF9zY29yZSAlPiUKICBmaWx0ZXIoZGlmZl9kYXlzICE9IDApICU+JQogIGdncGxvdChhZXMoeCA9IGRpZmZfZGF5cywgeSA9IEZLLCBzaXplID0gYmFja2Vyc19jb3VudCkpKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIGFlcyhjb2wgPSBhcy5mYWN0b3Ioc3RhdGUpKSkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygxLCAxMCkpKwogIGxhYnMoeCA9ICJEYXlzIEJldHdlZW4gU3RhdGUgQ2hhbmdlIiwgeSA9ICJGbGVzY2gtS2luY2FpZCBHcmFkZSBMZXZlbCIsIHNpemUgPSAiQmFja2VycyBDb3VudCIsIGNvbCA9IlN0YXRlIikrCiAgdGhlbWVfYncoKQpgYGAKCjMpIEkgY29udGludWUgdG8gZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHJlYWRhYmlsaXR5IG1lYXN1cmUgYW5kIHN1Y2Nlc3NmdWwgcHJvamVjdHMuIEFzIHRoZSBmaXR0ZWQgbGluZSBzaG93cywgd2hlbiB0aGUgZGVzY3JpcHRpb25zIGJlY29tZSBoYXJkZXIgdG8gcmVhZCwgdGhlIGJhY2tlciBjb3VudHMgaW5jcmVhc2VkIGEgbGl0dGxlIGJpdC4gQnV0IGFjdHVhbGx5LCB0aGUgZWZmZWN0IGlzIHRvbyB0aW55IHRvIG9ic2VydmUgZGlyZWN0bHkuIEJlc2lkZXMsIHRoZSBzaXplIG9mIGNpcmNsZXMgcmVwcmVzZW50cyB0aGUgYWNoaWV2ZW1lbnQgcmF0aW8gb2YgcHJvamVjdHMuIFdlIGNhbiBmaW5kIHRoYXQgZGVzY3JpcHRpb25zIG9mIHNvbWUgcHJvamVjdHMgd2l0aCB2ZXJ5IGhpZ2ggYWNoaWV2ZW1lbnQgcmF0aW8gaGF2ZSBhIGxvdyBGbGVzY2gtS2luY2FpZCBHcmFkZSBMZXZlbCwgd2hpY2ggbWVhbnMgZWFzaWVyIHRvIHJlYWQuCmBgYHtyfQpsaWJyYXJ5KGdndGhlbWVzKQpyZWFkX3Njb3JlICU+JQogIGZpbHRlcihiYWNrZXJzX2NvdW50ICE9MCkgJT4lCiAgZ2dwbG90KGFlcyh4PSBiYWNrZXJzX2NvdW50LCB5ID0gRkssIHNpemUgPSBhY2hpZXZlbWVudF9yYXRpbykpKwogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIGFlcyhjb2wgPSB0b3BfY2F0ZWdvcnkpKSsKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDEwKSkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2dhbScsIGNvbCA9ICJibGFjayIsIHNpemUgPSAwLjgpICsKICBndWlkZXMoc2l6ZSA9IEZBTFNFKSArCiAgdGhlbWVfdHVmdGUoKSArCiAgeGxhYigiTG9nIEJhY2tlcnMgQ291bnQiKSArIHlsYWIoIkZsZXNjaC1LaW5jYWlkIEdyYWRlIExldmVsIikKYGBgCgoKIyMjIDMuIFNlbnRpbWVudAoKTm93LCBsZXQncyBjaGVjayB3aGV0aGVyIHRoZSB1c2Ugb2YgcG9zaXRpdmUgLyBuZWdhdGl2ZSB3b3JkcyBvciBzcGVjaWZpYyBlbW90aW9ucyBoZWxwcyBhIHByb2plY3QgdG8gYmUgc3VjY2Vzc2Z1bC4gCgojIyMjIGEpIFN0YXkgcG9zaXRpdmUKCkNhbGN1bGF0ZSB0aGUgdG9uZSBvZiBlYWNoIHRleHQgYmFzZWQgb24gdGhlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB3b3JkcyB0aGF0IGFyZSBiZWluZyB1c2VkLkkgdXNlIHRoZSBCaW5nIGRpY3Rpb25hcnkgY29udGFpbmVkIGluIHRoZSB0aWR5dGV4dCBwYWNrYWdlIChgdGlkeXRleHQ6OnNlbnRpbWVudHNgKS4gVmlzdWFsaXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0b25lIG9mIHRoZSBkb2N1bWVudCBhbmQgc3VjY2Vzcy4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KGJyb29tKQpiaW5nIDwtIGdldF9zZW50aW1lbnRzKCJiaW5nIikKc19zZW50IDwtIHRpZHkoRG9jdW1lbnRUZXJtTWF0cml4KHNfY2xlYW4pKQpmX3NlbnQgPC0gdGlkeShEb2N1bWVudFRlcm1NYXRyaXgoZl9jbGVhbikpCmBgYAoKMSkgSSBpbm5lciBqb2luIHRoZSBiaW5nIGxleGljb24gdG8gZGV0ZXJtaW5lIGlmIHRoZSB3b3JkcyBpbiB0aGUgZGVzY3JpdGlvbnMgYXJlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZS4gQWZ0ZXIgc3VtbWFyaXppbmcgdGhlIHRvdGFsIG51bWJlciBvZiBwb3NzaXRpdmUgd29yZHMgYW5kIG5lZ2F0aXZlIHdvcmRzLCBJIGNhbGN1bGF0ZWQgdGhlIHBvbGFyaXR5LCB3aGljaCBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIG51bWJlciBvZiBwb3NpdGl2ZSB3b3JkcyBhbmQgbmVnYXRpdmUgd29yZHMgZm9yIGVhY2ggZGVzY3JpcHRpb24uCmBgYHtyfQpzX3BvbGFyaXR5IDwtIHNfc2VudCAlPiUKICBpbm5lcl9qb2luKGJpbmcsIGJ5ID0gYygidGVybSI9IndvcmQiKSkgJT4lCiAgbXV0YXRlKGluZGV4ID0gYXMubnVtZXJpYyhkb2N1bWVudCkpICU+JQogIGNvdW50KHNlbnRpbWVudCwgaW5kZXgpICU+JQogIHNwcmVhZChzZW50aW1lbnQsbixmaWxsID0gMCkgJT4lCiAgbXV0YXRlKHBvbGFyaXR5ID0gcG9zaXRpdmUgLSBuZWdhdGl2ZSkKZl9wb2xhcml0eSA8LSBmX3NlbnQgJT4lCiAgaW5uZXJfam9pbihiaW5nLCBieSA9IGMoInRlcm0iPSJ3b3JkIikpICU+JQogIG11dGF0ZShpbmRleCA9IGFzLm51bWVyaWMoZG9jdW1lbnQpKSAlPiUKICBjb3VudChzZW50aW1lbnQsIGluZGV4KSAlPiUKICBzcHJlYWQoc2VudGltZW50LG4sZmlsbCA9IDApICU+JQogIG11dGF0ZShwb2xhcml0eSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpCnNfcG9sYXJpdHkkc3RhdGUgPC0gInN1Y2Nlc3NmdWwiCmZfcG9sYXJpdHkkc3RhdGUgPC0gImZhaWxlZCIKYGBgCgpgYGB7cn0KYWxsX3BvbGFyaXR5IDwtIHJiaW5kKHNfcG9sYXJpdHksZl9wb2xhcml0eSkKYWxsX3BvbGFyaXR5JHN0YXRlIDwtIGFzLmZhY3RvcihhbGxfcG9sYXJpdHkkc3RhdGUpCmhlYWQoYWxsX3BvbGFyaXR5KQpgYGAKCjIpIEFsbCBkZXNjcmlwdGlvbnMsIHdoZXRoZXIgc3VjY2Vzc2Z1bCBvciBmYWlsZWQsIHNob3cgYSBraW5kIG9mIHBvc2l0aXZlIHRvbmUgc2luY2UgdGhlaXIgcG9sYXJpdHkgc2NvcmUgYXJlIGFsbCBwb3NpdGl2ZSBudW1iZXJzLiBJbiBhdmVyYWdlLCB3ZSBjYW4gc2F5IGZhaWxlZCBwcm9qZWN0cyBoYXZlIGEgaGlnaGVyIHBvc2l0aXZlIHRvbmUgaW4gZGVzY3JpcHRpb25zIHRoYW4gdGhhdCBvZiBzdWNjZXNzZnVsIHByb2plY3RzLiBUaGlzIG1pZ2h0IGJlIGEgcmVhc29uIGZvciBmYWlsdXJlLCB3aGljaCBtZWFucyB0aGUgZGVzY3JpcHRpb25zIGFyZSB0b28gaWRlYWwgYW5kIGRvIG5vdCB0YWtlIGEgbG90IG9mIHJlYWwgYW5kIHBsYXVzaWJsZSBhbmFseXNpcyBpbnRvIGFjY291bnQuIFBlb3BsZSBhcmUgbGVzcyBsaWtlbHkgdG8gc3VwcG9ydCBhIHBvcmplY3Qgd2l0aCBhIGhpZ2ggZ29hbCBidXQgbGFjayBvZiBzcGVjaWZjIHBsYW4uCmBgYHtyfQpnZ3Bsb3QoYWxsX3BvbGFyaXR5LCBhZXMoaW5kZXgscG9sYXJpdHkpKSsKICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBzdGF0ZSwgZmlsbCA9IHN0YXRlKSwgbWV0aG9kID0gImxvZXNzIikgKyAKICBzY2FsZV9jb2xvcl92aXJpZGlzKGRpc2NyZXRlID0gVFJVRSwgb3B0aW9uID0gIkQiKSsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiUHJvamVjdCBEZXNjcmlwdGlvbiBQb2xhcml0eSIpKwogIGxhYnMoeCA9ICJQcm9qZWN0IE5vLiIsIHk9IlBvc2l0aXZlIFBvbGFyaXR5IikrCiAgdGhlbWVfZWNvbm9taXN0KCkrCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpKQpgYGAKCiMjIyMgYikgUG9zaXRpdmUgdnMgbmVnYXRpdmUKClNlZ3JlZ2F0ZSBhbGwgMiwwMDAgYmx1cmJzIGludG8gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHRleHRzIGJhc2VkIG9uIHRoZWlyIHBvbGFyaXR5IHNjb3JlIGNhbGN1bGF0ZWQgaW4gc3RlcCAoYSkuIE5vdywgY29sbGFwc2UgdGhlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB0ZXh0cyBpbnRvIHR3byBsYXJnZXIgZG9jdW1lbnRzLiBDcmVhdGUgYSBkb2N1bWVudC10ZXJtLW1hdHJpeCBiYXNlZCBvbiB0aGlzIGNvbGxhcHNlZCBzZXQgb2YgdHdvIGRvY3VtZW50cy4gR2VuZXJhdGUgYSBjb21wYXJpc29uIGNsb3VkIHNob3dpbmcgdGhlIG1vc3QtZnJlcXVlbnQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzLiAgCmBgYHtyfQojIENvbWJpbmUgYWxsIGRlc2NyaXB0aW9ucyBvZiBzdWNjZXNzZnVsIHByb2plY3RzIGFuZCBmYWlsZWQgb25lcy4gCiMgQ3JlYXRlIGEgZGF0YXNldCB3aGljaCBqdXN0IGhhdmUgdHdvIGNvbHVtbnMsIG9uZSBpcyB0ZXh0LCBhbm90aGVyIGlzIHBvbGFyaXR5CnMgPC0gc3VjY2Vzc2Z1bCAlPiUKICBtdXRhdGUoaW5kZXggPSByb3dfbnVtYmVyKCkpJT4lCiAgc2VsZWN0KGJsdXJiLCBpbmRleCkKc3MgPC0gc19wb2xhcml0eSAlPiUKICBsZWZ0X2pvaW4ocyxieT0iaW5kZXgiKSU+JQogIHNlbGVjdChibHVyYixwb2xhcml0eSkKZiA8LSBmYWlsICU+JQogIG11dGF0ZShpbmRleCA9IHJvd19udW1iZXIoKSklPiUKICBzZWxlY3QoYmx1cmIsIGluZGV4KQpmZiA8LSBmX3BvbGFyaXR5ICU+JQogIGxlZnRfam9pbihmLGJ5PSJpbmRleCIpJT4lCiAgc2VsZWN0KGJsdXJiLHBvbGFyaXR5KQp0b3RhbCA8LSByYmluZChzcyxmZikKdG90YWxfZGYgPC0gdG90YWwgJT4lCiAgc2VsZWN0KHRleHQgPSBibHVyYiwgcG9sYXJpdHkgPSBwb2xhcml0eSkKaGVhZCh0b3RhbF9kZikKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBmdW5jdGlvbiB0aGF0IGNhbiBzcGxpdCB0aGUgcG9zaXRpdmUgcG9sYXJpdHkgYW5kIG5lZ2F0aXZlIHBvbGFyaXR5IHRleHQKIyBQYXN0ZSBhbGwgcG9zaXRpdmUgdGV4dHMgYXMgb25lIGxvbmcgc3RyaW5nLCBwYXN0ZSBhbGwgbmVnYXRpdmUgdGV4dHMgYXMgYW5vdGhlciBsb25nIHN0cmluZwpzdWJzZWN0aW9uIDwtIGZ1bmN0aW9uKGRmKXsKICB4LnBvcyA8LSBzdWJzZXQoZGYkdGV4dCwgZGYkcG9sYXJpdHkgPiAwKQogIHgubmVnIDwtIHN1YnNldChkZiR0ZXh0LCBkZiRwb2xhcml0eSA8IDApCiAgeC5wb3MgPC0gcGFzdGUoeC5wb3MsIGNvbGxhcHNlID0gIiAiKQogIHgubmVnIDwtIHBhc3RlKHgubmVnLCBjb2xsYXBzZSA9ICIgIikKICBhbGwudGVybXMgPC0gYyh4LnBvcywgeC5uZWcpCiAgcmV0dXJuKGFsbC50ZXJtcykKfQphbGxfdGVybXMgPC0gc3Vic2VjdGlvbih0b3RhbF9kZikKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBwb2xhcml0eSB3b3JkY2xvdWQKcG9sYXJpdHlfY29ycHVzIDwtIGFsbF90ZXJtcyAlPiUKICBWZWN0b3JTb3VyY2UoKSAlPiUKICBWQ29ycHVzKCkKCmFsbF90ZG0gPC1UZXJtRG9jdW1lbnRNYXRyaXgocG9sYXJpdHlfY29ycHVzLCAKICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KHJlbW92ZVB1bmN0dWF0aW9uID0gVFJVRSwgc3RvcHdvcmRzID0gc3RvcHdvcmRzKCJlbiIpKSkgJT4lCiAgICAgICAgICAgIGFzLm1hdHJpeCgpCmNvbG5hbWVzKGFsbF90ZG0pIDwtIGMoInBvc2l0aXZlIiwgIm5lZ2F0aXZlIikKcG9sYXJpdHlfY2xvdWQgPC0gY29tcGFyaXNvbi5jbG91ZChhbGxfdGRtLCBtYXgud29yZHMgPSA4MCwgY29sb3JzID0gYygiZ29sZGVucm9kMSIsImRhcmtvbGl2ZWdyZWVuNCIpKQpgYGAKCiMjIyMgYykgR2V0IGluIHRoZWlyIG1pbmQKCk5vdywgdXNlIHRoZSBOUkMgV29yZC1FbW90aW9uIEFzc29jaWF0aW9uIExleGljb24gaW4gdGhlIHRpZHl0ZXh0IHBhY2thZ2UgdG8gaWRlbnRpZnkgYSBsYXJnZXIgc2V0IG9mIGVtb3Rpb25zIChhbmdlciwgYW50aWNpcGF0aW9uLCBkaXNndXN0LCBmZWFyLCBqb3ksIHNhZG5lc3MsIHN1cnByaXNlLCB0cnVzdCkuIEFnYWluLCB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB1c2Ugb2Ygd29yZHMgZnJvbSB0aGVzZSBjYXRlZ29yaWVzIGFuZCBzdWNjZXNzLiBXaGF0IGlzIHlvdXIgZmluZGluZz8KCjEpIEdldCB0aGUgbnJjIGxleGljb24KYGBge3J9CmxpYnJhcnkodGV4dGRhdGEpCmxpYnJhcnkodGlkeXRleHQpCgpucmMgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpCmhlYWQobnJjKQpgYGAKCjIpIFNob3cgYWxsIHRva2VucyB3aXRoIHRoZSBzdGF0ZSBvZiB0aGUgcHJvamVjdCB0aGV5IGNvbWUgZnJvbS4KYGBge3J9CnNfc2VudCRzdGF0ZSA8LSJzdWNjZXNzZnVsIgpmX3NlbnQkc3RhdGUgPC0iZmFpbGVkIgphbGxfZW1vdGlvbiA8LSByYmluZChzX3NlbnQsIGZfc2VudCkKaGVhZChhbGxfZW1vdGlvbikKYGBgCgozKSBJbm5lciBqb2luIHRoZSB0b2tlbnMgZGF0YXNldCB3aXRoIG5yYyBsZXhpY29uLiBJIGRyb3BwZWQgdGhvc2UgdG9rZW5zIHdpdGggcG9zaXRpdmUvbmVnYXRpdmUgc2VudGltZW50IGFuZCBqdXN0IGtlcHQgdGhvc2UgdG9rZW5zIHdpdGggc3BlY2lmaWMgZW1vdGlvbnMuIFRoZW4sIEkgY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgdG9rZW5zIGZvciBhbGwgZW1vdGlvbnMgaW4gdHdvIGtpbmRzIG9mIHN0YXRlLCBpbmRpdmlkdWFsbHkuCmBgYHtyfQpzY29yZXMgPC0gYWxsX2Vtb3Rpb24gJT4lCiAgaW5uZXJfam9pbihucmMsIGJ5ID0gYygidGVybSI9IndvcmQiKSkgJT4lCiAgZmlsdGVyKCFncmVwbCgicG9zaXRpdmV8bmVnYXRpdmUiLHNlbnRpbWVudCkpICU+JQogIGNvdW50KHN0YXRlLCBzZW50aW1lbnQpICU+JQogIHNwcmVhZChzdGF0ZSxuKQpzY29yZXMKYGBgCgo0KSBJIGNyZWF0ZSBhIFJhZGFyIHBsb3QgdGhhdCBzaG93cyA4IGVtb3Rpb25zLiBUaGUgZmluZGluZyBpcyBpbnRlcmVzdGluZyB0aGF0IHRoZSBkZXNjcmlwdGlvbnMgb2YgZmFpbGVkIHByb2plY3RzIGFuZCBzdWNjZXNzZnVsIHByb2plY3RzIHNoYXJlIHNpbWlsYXIgc2hhcGUgc2VudGltZW50IHJhZGFyLiBUaGV5IGJvdGggZW1waGFzaXplIG1vcmUgb24gc29tZSBlbW90aW9ucyBsaWtlIGpveSwgdHJ1c3QgYW5kIGFudGljaXBhdGlvbi4gQnV0IHRoZSBwcm9ibGVtIG9mIGZhaWxlZCBwcm9qZWN0cyBpcyB0aGF0IHRoZXkgYW1wbGlmeSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiBtYWluIGVtb3Rpb25zIGFuZCBvdGhlciBjb21wbGljYXRlZCBlbW90aW9ucywgc28gdGhleSBjYW4gY29udmV5IHBlb3BsZSBqdXN0IGEgam95ZnVsIGFuZCBwZWFjZWZ1bCBmZWVsLiBPbiB0aGUgb3RoZXIgaGFuZCwgc3VjY2Vzc2Z1bCBiYWxhbmNlIGFsbCBlbW90aW9ucyBiZXR0ZXIuIFRoZSBkZXNjcmlwdGlvbnMgc2hvdyBtb3JlIHZhcmlvdXMgYW5kIG11bHRpZGltZW5zaW9uYWwgZW1vdGlvbnMsIHdoaWNoIG1ha2UgdGhlIHByb2plY3RzIHNlZW0gdG8gbW9yZSByZWFsaXN0aWMgYW5kIHJlbGlhYmxlLiAKYGBge3J9CmxpYnJhcnkocmFkYXJjaGFydCkKbGlicmFyeSh3ZWJzaG90KQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQpsaWJyYXJ5KHBuZykKcmFkYXJQbG90IDwtIGNoYXJ0SlNSYWRhcihzY29yZXMsIHNob3dUb29sVGlwTGFiZWw9VFJVRSwgbWFpbiA9ICJTZW50aW1lbnQgUmFkYXIiKQpzYXZlV2lkZ2V0KHJhZGFyUGxvdCwgInJhZGFycGx0Lmh0bWwiKQp3ZWJzaG90KCJyYWRhcnBsdC5odG1sIikKaW1nIDwtIG1hZ2ljazo6aW1hZ2VfcmVhZCgid2Vic2hvdC5wbmciKQpwbG90KGltZykKYGBgCiMjIFN1Ym1pc3Npb24KClBsZWFzZSBmb2xsb3cgdGhlIFtpbnN0cnVjdGlvbnNdKC9FeGVyY2lzZXMvaG9tZXdvcmtfc3VibWlzc2lvbl9pbnN0cnVjdGlvbnMubWQpIHRvIHN1Ym1pdCB5b3VyIGhvbWV3b3JrLiBUaGUgaG9tZXdvcmsgaXMgZHVlIG9uIFRodXJzZGF5LCBBcHJpbCAxNi4KCiMjIFBsZWFzZSBzdGF5IGhvbmVzdCEKCklmIHlvdSBkbyBjb21lIGFjcm9zcyBzb21ldGhpbmcgb25saW5lIHRoYXQgcHJvdmlkZXMgcGFydCBvZiB0aGUgYW5hbHlzaXMgLyBjb2RlIGV0Yy4sIHBsZWFzZSBubyB3aG9sZXNhbGUgY29weWluZyBvZiBvdGhlciBpZGVhcy4gV2UgYXJlIHRyeWluZyB0byBldmFsdWF0ZSB5b3VyIGFiaWxpdGllcyB0byB2aXN1YWxpemVkIGRhdGEgbm90IHRoZSBhYmlsaXR5IHRvIGRvIGludGVybmV0IHNlYXJjaGVzLiBBbHNvLCB0aGlzIGlzIGFuIGluZGl2aWR1YWxseSBhc3NpZ25lZCBleGVyY2lzZSAtLSBwbGVhc2Uga2VlcCB5b3VyIHNvbHV0aW9uIHRvIHlvdXJzZWxmLiAK